The CL package defines the
following macro which more closely follows the Common Lisp
let form:
This form is exactly like
letexcept that the bindings it establishes are purely lexical. Lexical bindings are similar to local variables in a language like C: Only the code physically within the body of thelexical-let(after macro expansion) may refer to the bound variables.(setq a 5) (defun foo (b) (+ a b)) (let ((a 2)) (foo a)) ⇒ 4 (lexical-let ((a 2)) (foo a)) ⇒ 7In this example, a regular
letbinding ofaactually makes a temporary change to the global variablea, sofoois able to see the binding ofato 2. Butlexical-letactually creates a distinct local variableafor use within its body, without any effect on the global variable of the same name.The most important use of lexical bindings is to create closures. A closure is a function object that refers to an outside lexical variable. For example:
(defun make-adder (n) (lexical-let ((n n)) (function (lambda (m) (+ n m))))) (setq add17 (make-adder 17)) (funcall add17 4) ⇒ 21The call
(make-adder 17)returns a function object which adds 17 to its argument. Iflethad been used instead oflexical-let, the function object would have referred to the globaln, which would have been bound to 17 only during the call tomake-adderitself.(defun make-counter () (lexical-let ((n 0)) (function* (lambda (&optional (m 1)) (incf n m))))) (setq count-1 (make-counter)) (funcall count-1 3) ⇒ 3 (funcall count-1 14) ⇒ 17 (setq count-2 (make-counter)) (funcall count-2 5) ⇒ 5 (funcall count-1 2) ⇒ 19 (funcall count-2) ⇒ 6Here we see that each call to
make-countercreates a distinct local variablen, which serves as a private counter for the function object that is returned.Closed-over lexical variables persist until the last reference to them goes away, just like all other Lisp objects. For example,
count-2refers to a function object which refers to an instance of the variablen; this is the only reference to that variable, so after(setq count-2 nil)the garbage collector would be able to delete this instance ofn. Of course, if alexical-letdoes not actually create any closures, then the lexical variables are free as soon as thelexical-letreturns.Many closures are used only during the extent of the bindings they refer to; these are known as “downward funargs” in Lisp parlance. When a closure is used in this way, regular Emacs Lisp dynamic bindings suffice and will be more efficient than
lexical-letclosures:(defun add-to-list (x list) (mapcar (lambda (y) (+ x y))) list) (add-to-list 7 '(1 2 5)) ⇒ (8 9 12)Since this lambda is only used while
xis still bound, it is not necessary to make a true closure out of it.You can use
defunorfletinside alexical-letto create a named closure. If several closures are created in the body of a singlelexical-let, they all close over the same instance of the lexical variable.The
lexical-letform is an extension to Common Lisp. In true Common Lisp, all bindings are lexical unless declared otherwise.